package hello;

import java.util.List;

import org.hibernate.FlushMode;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.ReplicationMode;
import org.hibernate.Session;
import org.hibernate.Transaction;

import persistence.HibernateUtil;
import auction.chapter08.ItemTriggerUpdate;
import auction.chapter08.UserCustomizeLoadSQL;
import auction.chapter08.UserNaturalKeyVersion;

public class HelloWorldPersistenceContext
{
	public static void main(String[] args)throws Exception{
		//testReplicate();
		test2();
		//testDetached();
		//testMerge();
		//testFlushModeManual();
		//testFlushModeCommit();
		//testFlushModeAlways();
		//testFlushModeAuto();
	}
	
	public static void testFlushModeCommit()throws Exception{
		Session s1=HibernateUtil.getSessionFactory().openSession();
		// The Session is flushed when Transaction.commit() is called
		s1.setFlushMode(FlushMode.COMMIT);
		out(s1.getFlushMode());
		Transaction tx1=s1.beginTransaction();
		
		ItemTriggerUpdate loadi=(ItemTriggerUpdate) s1.get(ItemTriggerUpdate.class, 1L);
		loadi.setTitle("uuuu");
		/*
		//Query this table won't trigger flush!!! 
		Query q=s1.createQuery("from ItemTriggerUpdate");
		List result=q.list();
		ItemTriggerUpdate loadi2=(ItemTriggerUpdate)result.get(0);
		out(loadi == loadi2);
		//this value should be stale value
		out(loadi2.getCreated());
		*/
		
		
		//Query to another table won't trigger flush!!! flush occur at commit!!!
		Query q=s1.createQuery("from UserCustomizeLoadSQL");
		List result=q.list();
		UserCustomizeLoadSQL loadu2=(UserCustomizeLoadSQL)result.get(0);
		out(loadi.getCreated());//Should be stale value
		
		
		out("before commit");
		tx1.commit();
		out("after commit");
		s1.close();
		
	}
	
	public static void testFlushModeAlways()throws Exception{
		Session s1=HibernateUtil.getSessionFactory().openSession();
		//The Session is flushed before every query. 
		//This is almost always unnecessary and inefficient. 
		s1.setFlushMode(FlushMode.ALWAYS);
		out(s1.getFlushMode());
		Transaction tx1=s1.beginTransaction();
		
		ItemTriggerUpdate loadi=(ItemTriggerUpdate) s1.get(ItemTriggerUpdate.class, 1L);
		loadi.setTitle("uuu");//trigger a flush
		
		Query q=s1.createQuery("from ItemTriggerUpdate");
		List result=q.list();
		ItemTriggerUpdate loadi2=(ItemTriggerUpdate)result.get(0);
		//out(loadi == loadi2);
		//this value is updated by trigger, should be NEW value,
		//BUT this is still stale value??? at this time,the Query only bind result to PK???
		out(loadi2.getCreated());
		
		/*
		//query another table, always trigger a flush
		Query q=s1.createQuery("from UserCustomizeLoadSQL");
		List result=q.list();
		UserCustomizeLoadSQL loadu2=(UserCustomizeLoadSQL)result.get(0);
		*/
		//this should be New value
		
		out("before commit");
		tx1.commit();
		out("after commit");
		s1.close();
		
	}
	
	public static void testFlushModeAuto()throws Exception{
		Session s1=HibernateUtil.getSessionFactory().openSession();
		//The Session is sometimes flushed before query execution in order to ensure that queries never 
		//return stale state.Default, if changes are held in memory that would affect the results of the query, 
		//hibernate synchronizes first
		s1.setFlushMode(FlushMode.AUTO);
		out(s1.getFlushMode());
		Transaction tx1=s1.beginTransaction();
		
		ItemTriggerUpdate loadi=(ItemTriggerUpdate) s1.get(ItemTriggerUpdate.class, 1L);
		loadi.setTitle("uuuu");
		
		//Query this table will trigger flush!!! before commit!!!
		Query q=s1.createQuery("from ItemTriggerUpdate");
		List result=q.list();
		ItemTriggerUpdate loadi2=(ItemTriggerUpdate)result.get(0);
		out(loadi == loadi2);
		//this value is updated by trigger, should be NEW value,
		out(loadi2.getCreated());//BUT this is still stale value???
		
		/*
		//Query to another table won't trigger flush!!! flush occur at commit!!!
		Query q=s1.createQuery("from UserCustomizeLoadSQL");
		List result=q.list();
		UserCustomizeLoadSQL loadu2=(UserCustomizeLoadSQL)result.get(0);
		out(loadi.getCreated());//Should be stale value
		*/
		
		out("before commit");
		tx1.commit();
		out("after commit");
		s1.close();
		
	}
	
	public static void testFlushModeManual()throws Exception{
		Session s1=HibernateUtil.getSessionFactory().openSession();
		s1.setFlushMode(FlushMode.MANUAL);
		out(s1.getFlushMode());
		Transaction tx1=s1.beginTransaction();
		
		/*
		//update not occur
		UserCustomizeLoadSQL loadu=(UserCustomizeLoadSQL)s1.load(UserCustomizeLoadSQL.class, 1L);
		loadu.setName("apple");
		s1.update(loadu);
		//s1.flush();
		*/
		
		//whether insert record depends on the ID generator
		//increment --> select max(id) ... No record
		//native(identity) --> id only generated after record insert.
		Message2 msg2=new Message2();
		msg2.setText("ww");
		Long id=(Long)s1.save(msg2);
		out(id);
		
		/*//delete not occur
		UserCustomizeLoadSQL loadu=(UserCustomizeLoadSQL)s1.get(UserCustomizeLoadSQL.class, 1L);
		s1.delete(loadu);
		*/
		
		tx1.commit();
		s1.close();
		
	}
	
	public static void testMerge() throws Exception{
		Session s1=HibernateUtil.getSessionFactory().openSession();
		Transaction tx1=s1.beginTransaction();
		
		UserCustomizeLoadSQL nu=new UserCustomizeLoadSQL();
		nu.setName("new u");
		nu.setAddress("new addr");
		
		UserCustomizeLoadSQL detachedU=(UserCustomizeLoadSQL)s1.get(UserCustomizeLoadSQL.class, 1L);
		//s1.delete(detachedU);
		tx1.commit();
		s1.close();
		
		Session s2=HibernateUtil.getSessionFactory().openSession();
		Transaction tx2=s2.beginTransaction();
		
		
		detachedU.setName("111");
		
		//UserCustomizeLoadSQL u2=(UserCustomizeLoadSQL)s2.get(UserCustomizeLoadSQL.class, 1L);
		UserCustomizeLoadSQL u3=(UserCustomizeLoadSQL)s2.merge(nu);
		
		
		
		tx2.commit();
		s2.close();
	}
	
	public static void testDetached() throws Exception{
		Session s1=HibernateUtil.getSessionFactory().openSession();
		Transaction tx1=s1.beginTransaction();
		
		UserCustomizeLoadSQL nu=new UserCustomizeLoadSQL();
		nu.setName("new u");
		nu.setAddress("new addr");
		
		UserCustomizeLoadSQL loadu=(UserCustomizeLoadSQL)s1.get(UserCustomizeLoadSQL.class, 1L);
		
		tx1.commit();
		s1.close();
		
		Session s2=HibernateUtil.getSessionFactory().openSession();
		Transaction tx2=s2.beginTransaction();
		
		/*
		loadu.setName("111");
		
		s2.update(loadu);
		loadu.setAddress("222");//Only one update SQL
		*/
		
		loadu.setName("111");
		
		s2.lock(nu, LockMode.NONE);
		//loadu.setAddress("222");
		
		tx2.commit();
		s2.close();
	}
	
	public static void test2()throws Exception{
		Session s1=HibernateUtil.getSessionFactory().openSession();
		Transaction tx1=s1.beginTransaction();
		
		UserCustomizeLoadSQL n=new UserCustomizeLoadSQL();
		n.setName("nnn");
		n.setAddress("save or persist");
		s1.persist(n);
		
		
		
		/*
		UserCustomizeLoadSQL loadu=(UserCustomizeLoadSQL)s1.load(UserCustomizeLoadSQL.class, 1L);
		out(loadu.getName());
		loadu.setName("updated name");
		UserCustomizeLoadSQL loadu2=(UserCustomizeLoadSQL)s1.load(UserCustomizeLoadSQL.class, 1L);
		out(loadu2.getName());
		out(loadu == loadu2);
		*/
		
		/*
		UserCustomizeLoadSQL loadu=(UserCustomizeLoadSQL)s1.load(UserCustomizeLoadSQL.class, 1L);
		s1.delete(loadu);
		*/
		
		/*
		UserCustomizeLoadSQL n=new UserCustomizeLoadSQL();
		n.setId(1L);
		s1.delete(n);
		*/
		
		tx1.commit();
		s1.close();
		
		
		Session s2=HibernateUtil.getSessionFactory().openSession();
        Transaction tx2=s2.beginTransaction();
		
        //loadu in session already
		UserCustomizeLoadSQL loadu=(UserCustomizeLoadSQL)s2.get(UserCustomizeLoadSQL.class, 1L);
        //loadu.setAddress("loadu set");
		
		UserCustomizeLoadSQL loadu2=new UserCustomizeLoadSQL();
		loadu2.setId(loadu.getId());
		loadu2.setAddress("xxx");
        s2.update(loadu2);//org.hibernate.NonUniqueObjectException throws
		
		/*
		List result=s2.createQuery("from UserCustomizeLoadSQL").list();
        UserCustomizeLoadSQL loadu2=(UserCustomizeLoadSQL)result.get(0);
        out(loadu == loadu2);
        */
        tx2.commit();
        s2.close();
		
	}

	
	public static void testReplicate()throws Exception{
		Session s=HibernateUtil.getSessionFactory().openSession();
		Transaction tx=s.beginTransaction();
		
		UserNaturalKeyVersion uv=(UserNaturalKeyVersion)s.get(UserNaturalKeyVersion.class, "wei");
		UserCustomizeLoadSQL u=(UserCustomizeLoadSQL)s.get(UserCustomizeLoadSQL.class, 1L);
		
		tx.commit();
		s.close();
		
		
		
		Session s1=HibernateUtil.getSessionFactory2().openSession();
		Transaction tx1=s1.beginTransaction();
		
		//s1.replicate(u, ReplicationMode.EXCEPTION);
		//s1.replicate(u, ReplicationMode.IGNORE);
		s1.replicate(u, ReplicationMode.LATEST_VERSION);
		//s1.replicate(u, ReplicationMode.OVERWRITE);
		
		tx1.commit();
		s1.close();
		
		
		
		//HibernateUtil.stat();
		HibernateUtil.shutdown();
	}
	
	public static void getParent(Class clazz)
	{
		Class parent=clazz.getSuperclass();
	  if(parent !=null)
	  {
			System.out.println("###"+parent.getName());
			getParent(parent);
		}
	}
	
	static void out(Object obj)
	{
		System.out.println("--->"+obj);
	}
}
